﻿/*
* 有关图片基本输入输出的头文件
* 1. struct node;
*    用于储存文件名的链表节点的数据结构
* 2. node* getFiles(char* filePath);
*    读取指定目录下所有文件及文件夹名
* 3. void load_image_from_file_folder(char* file_path, IMAGE** img, int* num);
*    加载指定目录下的所有图片，主要用于制作逐帧动画
* 4. void draw_png(const int picture_x, const int picture_y, IMAGE* picture);
*    绘制带透明色的 png 图片
*/

#pragma once

#include <Windows.h>
#include <io.h>

// 定义链表上结点的数据结构
struct node
{
	char* name; // 定义指向文件名的字符串指针 //char name[_MAX_FNAME]; // 定义储存文件名的字符串数组，_MAX_FNAME表示文件名的最大长度
	node* next; // 定义指向下一节点的指针
};

/*
 * 读取指定目录下所有文件的文件名
 * char* filePath：指定的目录，支持相对路径，支持通配符
 * int *num：指定文件的数量
 * node* getFiles(char* filePath,int *num)：返回指定目录下所有文件名的链表
 */
static node* get_files(char* file_path, int* num)
{
	// 参数格式的简单检查
	if (*file_path == '\0')
	{
		MessageBox(nullptr, TEXT(R"(载入参数为空。)"), TEXT(R"(错误！)"), MB_OK); // 显示错误弹窗
		exit(0);
	}

	*num = 0;

	// 计算filePath中的文件位置长度filePathFront_length
	auto* p = file_path; // 定义临时变量p记录filePath的值，防止filePath的值改变
	char* last = nullptr; // 定义储存filePath最后一次出现\位置的指针
	while (*p != '\0') // 当filePath未结束时
	{
		if (*p == '\\') // 遇到\时
		{
			last = p; // 记录下\的位置
		}
		p++; // 下一个字符
	}
	const auto file_path_front_length = last - file_path + 1;

	// 储存filePath中的文件位置filePathFront
	auto* file_path_front = new char[file_path_front_length + 2]; // 防止缓冲区溢出漏洞
	auto i = 0;
	while (file_path + i != last) // 当p未到filePath最后一次出现\的位置时
	{
		file_path_front[i] = *(file_path + i);
		i++;
	}
	file_path_front[i] = '\\'; // 补上漏掉的\号
	file_path_front[i + 1] = '\0'; // 补上结束符

	// 用链表储存filePath路径下所有文件的文件名
	node* head = nullptr; // 初始化表头
	auto* p2 = new node[sizeof(node)]; // 初始化链表指针p2
	auto* a = const_cast<char*>(R"(.)"); // 定义排除文件名"."
	auto* b = const_cast<char*>(R"(..)"); // 定义排除文件名".."
	auto* file_info = new __finddata64_t; // 定义存储文件各种信息的变量fileInfo，32位下使用 _finddata_t
	const auto h_file = _findfirst64(file_path, file_info); // 定义查找文件的句柄，32位下使用 _findfirst()
	if (h_file == -1) // 若失败，返回值为-1
	{
		MessageBox(nullptr, TEXT(R"(可能的解决方案：
1、请检查载入目录是否正确。
2、请检查程序是否有载入目录的读取权限。)"), TEXT(R"(错误！)"), MB_OK); // 显示错误弹窗
		exit(0);
	}
	do
	{
		if (strcmp(file_info->name, a) != 0 && strcmp(file_info->name, b) != 0 && file_info->attrib != _A_SUBDIR)
			// 跳过文件名"."、文件名".."和文件夹
		{
			auto* p1 = new node[sizeof(node)]; // 初始化/更新指针p1 //p1 = (node*)malloc(sizeof(node));
			p1->name = new char[file_path_front_length + strlen(file_info->name) + 1]; // 为name分配储存空间大小=路径长度+文件名长度+结束符长度
			strcpy_s(p1->name, file_path_front_length + strlen(file_info->name) + 1, file_path_front);
			// 将文件路径传递给p1->name
			strcat_s(p1->name, file_path_front_length + strlen(file_info->name) + 1, file_info->name); // 将文件路径和文件名拼接
			if (head == nullptr) // 链表为空，将该结点设置为表头
			{
				head = p1;
			}
			else // 链表非空，将该结点设加入到链表的末尾
			{
				p2->next = p1;
			}
			p2 = p1; // 记录在p1更新前记录p1的值
			(*num)++;
		}
	}
	while (_findnext64(h_file, file_info) == 0); // 依据句柄，查找下一个满足条件的文件名，返回值为0时继续查找，返回-1时查找完毕，结束查找，，32位下使用 _findnext()
	p2->next = nullptr; // 以空指针结束
	_findclose(h_file); // 关闭文件句柄

	// 检查目录下是否有文件
	if (head == nullptr)
	{
		MessageBox(nullptr, TEXT(R"(请检查载入目录下是否有文件)"), TEXT(R"(错误！)"), MB_OK); // 显示错误弹窗
		exit(0);
	}

	return head; // 返回链表表头指针
}

/*
 * 加载指定目录下的所有图片，主要用于制作逐帧动画
 * char* filePath：指定目录，要求目录下就是图片文件，支持通配符
 * IMAGE** img：IMAGE类指针的地址
 * int* num：记录加载图片数量
 */
static void load_image_from_file_folder(char* file_path, IMAGE** img, int* num)
{
	*num = 0; // 图片数量初始化为0
	auto* head = get_files(file_path, num); // 得出指定目录下文件名，储存在链表中

	// 遍历文件名链表
	auto* p = head;
	*img = new IMAGE[*num]; // 根据num的值动态调整数组大小
	auto i = 0;
	auto* img_p = *img;
	for (; i < *num; i++) // 依次载入图片
	{
		const auto name_length = strlen(p->name); // 计算文件名长度
		auto* u_name = new wchar_t[name_length + 1]; // 定义储存Unicode码的字符串
		swprintf(u_name, name_length * 2, _T("%hs"), p->name); // 将ASCII码的字符串转化为Unicode码
		loadimage(img_p, u_name); // 载入图片
		img_p++;
		p = p->next;
	}
}

/*
 * 绘制带透明色的 png 图片
 * const int picture_x：图像的 x 坐标
 * const int picture_y：图像的 y 坐标
 * IMAGE* picture：储存图片的变量
 */
static void draw_png(const int picture_x, const int picture_y, IMAGE* picture) //x为载入图片的X坐标，y为Y坐标
{
	if (picture == nullptr) // 若载入图片为空
	{
		return; // 函数直接返回
	}
	
	// 变量初始化
	auto* dst = GetImageBuffer(); // GetImageBuffer()函数，用于获取绘图设备的显存指针
	auto* draw = GetImageBuffer();
	auto* src = GetImageBuffer(picture); //获取picture的显存指针
	const auto picture_width = picture->getwidth(); //获取picture的宽度
	const auto picture_height = picture->getheight(); //获取picture的高度
	const auto graph_width = getwidth(); //获取绘图区的宽度
	const auto graph_height = getheight(); //获取绘图区的高度

	// 实现透明贴图公式：Cp=αp*FP+(1-αp)*BP，用贝叶斯定理来进行点颜色的概率计算
	for (auto iy = 0; iy < picture_height; iy++)
	{
		for (auto ix = 0; ix < picture_width; ix++)
		{
			const auto src_x = ix + iy * picture_width; //在显存里像素的角标
			const auto sa = static_cast<int>((src[src_x] & 0xff000000) >> 24); //0xAArrggbb;AA是透明度
			const auto sr = static_cast<int>((src[src_x] & 0xff0000) >> 16); //获取RGB里的R
			const auto sg = static_cast<int>((src[src_x] & 0xff00) >> 8); //G
			const auto sb = static_cast<int>(src[src_x] & 0xff); //B
			if (ix + picture_x >= 0 && ix + picture_x < graph_width && iy + picture_y >= 0 && iy + picture_y <
				graph_height)
			{
				const auto dst_x = (ix + picture_x) + (iy + picture_y) * graph_width; //在显存里像素的角标
				const auto dr = static_cast<int>((dst[dst_x] & 0xff0000) >> 16);
				const auto dg = static_cast<int>((dst[dst_x] & 0xff00) >> 8);
				const auto db = static_cast<int>(dst[dst_x] & 0xff);
				draw[dst_x] = ((sr * sa / 255 + dr * (255 - sa) / 255) << 16)
					//公式： Cp=αp*FP+(1-αp)*BP  ； αp=sa/255 , FP=sr , BP=dr
					| ((sg * sa / 255 + dg * (255 - sa) / 255) << 8) //αp=sa/255 , FP=sg , BP=dg
					| (sb * sa / 255 + db * (255 - sa) / 255); //αp=sa/255 , FP=sb , BP=db
			}
		}
	}
}

